package com.lechi.web.listener;

import com.alibaba.fastjson2.JSON;
import com.lechi.iot.netty.channel.ITcpChannel;
import com.lechi.iot.netty.listener.TcpListener;
import com.lechi.iot.netty.message.TcpInMessage;
import com.lechi.iot.netty.message.TcpMessage;
import com.lechi.iot.netty.utils.BytesToHexUtil;
import com.lechi.iot.netty.utils.NettyCacheUtils;
import com.lechi.web.common.utils.DateUtils;
import com.lechi.web.common.utils.StringUtils;
import com.lechi.web.constants.Constants;
import com.lechi.web.constants.TcpMessageConstants;
import com.lechi.web.dto.IotDeviceCmdParseDto;
import com.lechi.web.framework.util.GuavaCacheUtil;
import com.lechi.web.framework.websocket.WebSocketServer;
import com.lechi.web.framework.websocket.WebSocketUtil;
import com.lechi.web.job.BatchInsertJob;
import com.lechi.web.system.domain.IotMonitorDataBo;
import com.lechi.web.util.AlarmRulesUtil;
import com.lechi.web.util.RecordFileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 指令监听器
 */
@Slf4j
@Component
public class TCPMessageListener implements TcpListener {

    private static final float offset = 0.005f;

    /**
     * Build data by bit sort byte [ ].
     *
     * @param bitSort    the bit sort
     * @param sourceData the source data
     * @return the byte [ ]
     */
    public static byte[] buildDataByBitSort(String bitSort, byte[] sourceData) {
        byte[] bytes = new byte[4];
        switch (bitSort) {
            case TcpMessageConstants.BADC_FORMAT: {
                bytes[0] = sourceData[1];
                bytes[1] = sourceData[0];
                bytes[3] = sourceData[2];
                bytes[2] = sourceData[3];
            }
            break;
            case TcpMessageConstants.DCBA_FORMAT: {
                bytes[0] = sourceData[3];
                bytes[1] = sourceData[2];
                bytes[2] = sourceData[1];
                bytes[3] = sourceData[0];
            }
            break;
            case TcpMessageConstants.CDAB_FORMAT: {
                System.arraycopy(sourceData, 2, bytes, 0, 2);
                System.arraycopy(sourceData, 0, bytes, 2, 2);
            }
            break;
            default:
                bytes = sourceData;
        }
        return bytes;
    }

    private static float hexToFloat(String hexString, Double intFactor, int digit) {
        int intBits = (int) Long.parseLong(hexString, 16);
        float v = 0;
        if (intFactor != null) {
            v = Float.intBitsToFloat(intBits) * Float.parseFloat(String.valueOf(intFactor));
        } else {
            v = Float.intBitsToFloat(intBits);
        }
        return Math.round(v * Math.pow(10, digit) + offset) / (float) Math.pow(10, digit);
    }

    private static float hexToInt(String hexString, double intFactor, int digit) {
        float v = Float.intBitsToFloat(Integer.parseInt(hexString, 16)) * Float.parseFloat(String.valueOf(intFactor));
        return Math.round(v * Math.pow(10, digit) + offset) / (float) Math.pow(10, digit);
    }

    @Override
    public void receive(ITcpChannel channel, TcpMessage message) {
        ITcpChannel tcpChannel = NettyCacheUtils.get(channel.hostName(), ITcpChannel.class);
        log.error("站点地址：{}, 原始报文:{}", channel.hostName(), BytesToHexUtil.bytesToHexString(message.toBytes()));
        if (tcpChannel == null || StringUtils.isBlank(tcpChannel.getDeviceSn())) {
            log.error("设备地址为空，获取管道失败或管道未注册设备，报文：{} 不解析！", BytesToHexUtil.bytesToHexString(message.toBytes()));
            return;
        }
        TcpInMessage tcpInMessage = (TcpInMessage) message;
        byte[] dataBytes = tcpInMessage.data().toBytes();
        byte[] dataArr = ArrayUtils.subarray(message.toBytes(), 0, 1);
        String addrHex = BytesToHexUtil.bytesToHexString(dataArr);
        String deviceSNAndAddr = tcpChannel.getDeviceSn() + "_" + addrHex;
        List cmdMsgList = NettyCacheUtils.get(deviceSNAndAddr, List.class);
        if (cmdMsgList == null || cmdMsgList.isEmpty()) {
            final Long deviceId = GuavaCacheUtil.getBiDeviceIdAndIPMap().inverse().get(channel.hostName());
            Map<String, Object> deviceDataMap = new HashMap<>();
            deviceDataMap.put("record", BytesToHexUtil.bytesToHexString(message.toBytes()));
            deviceDataMap.put("time", DateUtils.getTime());
            WebSocketServer.sendInfo(deviceId, JSON.toJSONString(deviceDataMap));
            log.error("报文中从站地址拼接设备序列号：{} 从缓存中没有获取到，该报文不解析!", deviceSNAndAddr);
            return;
        }
        List<IotMonitorDataBo> tcpDeviceDataList = new ArrayList<>();
        for (Object object : cmdMsgList) {
            IotDeviceCmdParseDto iotDeviceCmdParseDto = (IotDeviceCmdParseDto) object;
            IotMonitorDataBo iotMonitorPageData = new IotMonitorDataBo();
            BeanUtils.copyProperties(iotDeviceCmdParseDto, iotMonitorPageData);

            byte[] valueBytes = ArrayUtils.subarray(dataBytes, iotDeviceCmdParseDto.getBeginIndex(), iotDeviceCmdParseDto.getEndIndex());
            if (valueBytes == null || valueBytes.length == 0) {
                log.error("指令解析报文失败，原始报文：{},解析结构：{}", BytesToHexUtil.bytesToHexString(tcpInMessage.toBytes()), JSON.toJSONString(iotDeviceCmdParseDto));
                continue;
            }
            byte[] changeValue = buildDataByBitSort(iotDeviceCmdParseDto.getBitSort(), valueBytes);
            String sensorValue = BytesToHexUtil.bytesToHexString(changeValue);
            float v = 0f;
            try {
                int digit = iotDeviceCmdParseDto.getSensorDigit() != null ? iotDeviceCmdParseDto.getSensorDigit().intValue() : 2;
                if (Constants.INTEGER_DATA_FORMAT_32.equals(iotDeviceCmdParseDto.getDataFormat()) || Constants.INTEGER_DATA_FORMAT_16.equals(iotDeviceCmdParseDto.getDataFormat())) {
                    v = hexToInt(sensorValue, iotDeviceCmdParseDto.getIntFactor(), digit);
                } else {
                    v = hexToFloat(sensorValue, iotDeviceCmdParseDto.getIntFactor(), digit);
                }
                iotMonitorPageData.setReceiveData(BytesToHexUtil.bytesToHexString(tcpInMessage.toBytes()));
                iotMonitorPageData.setDoubleValue(v);
                iotMonitorPageData.setDateTime(new Date());
                iotMonitorPageData.setBaseTime(DateUtils.getBaseTime());
                iotMonitorPageData.setTableName("iot_monitor_data_" + iotMonitorPageData.getDeviceId());
                BatchInsertJob.workQueue.put(iotMonitorPageData);
                tcpDeviceDataList.add(iotMonitorPageData);
            } catch (Exception e) {
                log.error("报文详情：{}, 16进制数据：{} deviceSNAndAddr:{}, 解析异常", BytesToHexUtil.bytesToHexString(tcpInMessage.toBytes()), sensorValue, deviceSNAndAddr, e);
                continue;
            }
            AlarmRulesUtil.alarmRuleChain(iotMonitorPageData);
            WebSocketUtil.sendToMonitorPage(iotMonitorPageData);
            GuavaCacheUtil.getMonitorDataMap().put(iotMonitorPageData.getDeviceId(), iotMonitorPageData.getSensorAliasId(), iotMonitorPageData);
            GuavaCacheUtil.getBiDeviceIdAndIPMap().forcePut(iotMonitorPageData.getDeviceId(), channel.hostName());
            GuavaCacheUtil.getDeviceLastDated().put(String.valueOf(iotMonitorPageData.getDeviceId()), new Date());
        }
        WebSocketUtil.sendToDevicePage(Constants.MODBUS_RTU, tcpDeviceDataList);
        RecordFileUtil.producerRecord(Constants.MODBUS_RTU, tcpDeviceDataList);
    }
}
